package fm.service.impl;

import com.alibaba.fastjson.JSON;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import fm.config.MainConfig;
import fm.dao.HibernateBaseDao;
import fm.dao.MongoBaseDao;
import fm.entity.WxUser;
import fm.exception.BizException;
import fm.mongo.MCondition;
import fm.mongo.MRel;
import fm.mongo.MongoTable;
import fm.service.UserService;
import fm.util.CommonUtils;
import fm.util.Constant;
import fm.util.HuanXinSdkUtil;
import fm.util.WechatUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.codec.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 类名：fm.service.impl.UserServiceImpl
 * 创建者： CM .
 * 创建时间：2016/3/7
 */
//@Service("userService")
public class UserServiceImpl implements UserService {
    private final static Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);

    private final static ConcurrentHashMap<Long, Map> USER_CACHE = new ConcurrentHashMap<>();


    @Autowired
    HibernateBaseDao baseDao;
    @Autowired
    MongoBaseDao mongoBaseDao;

    public long saveOrUpdate(WxUser user) {
        WxUser src = getByOpenIdAndPublicNumberId(user.getOpenid(), user.getPublicNumberId());
        if (CommonUtils.isEmpty(src)) {
            LOGGER.info("add user : {}  ", JSON.toJSON(user));
            baseDao.save(user);
        } else {
            LOGGER.info("update user : {} ,src :{}", JSON.toJSON(user), JSON.toJSONString(src));
            if (!CommonUtils.isEmpty(user.getSubscribed())) {
                src.setSubscribed(user.getSubscribed());
            }
            if (!CommonUtils.isEmpty(user.getRemark())) {
                src.setRemark(user.getRemark());
            }
            if (!CommonUtils.isEmpty(user.getCity())) {
                src.setCity(user.getCity());
            }
            if (!CommonUtils.isEmpty(user.getCountry())) {
                src.setCountry(user.getCountry());
            }
            if (!CommonUtils.isEmpty(user.getNickname())) {
                src.setNickname(user.getNickname());
            }
            if (!CommonUtils.isEmpty(user.getProvince())) {
                src.setProvince(user.getProvince());
            }
            if (!CommonUtils.isEmpty(user.getPrivilege())) {
                src.setPrivilege(user.getPrivilege());
            }
            if (!CommonUtils.isEmpty(user.getTelephone())) {
                src.setTelephone(user.getTelephone());
            }
            if (!CommonUtils.isEmpty(user.getHxUsername())) {
                src.setHxUsername(user.getHxUsername());
            }
            if (!CommonUtils.isEmpty(user.getHxPassword())) {
                src.setHxPassword(user.getHxPassword());
            }
            baseDao.update(src);
        }
        return user.getId();
    }


    public WxUser getByOpenIdAndUuid(String openId, String uuid) {
        String hql = "select user from WxUser user,PublicNumber pn where user.openid=? and user.publicNumberId=pn.id and pn.uuid=?";
        return (WxUser) baseDao.getUnique(hql, openId, uuid);
    }

    @Override
    public WxUser getByOpenId(String openId) {
        String hql = "from WxUser where openId = ? or iosOpenid = ? or androidOpenid = ?";
        List items = baseDao.queryForList(hql, openId, openId, openId);
        WxUser user = CollectionUtils.isNotEmpty(items) ? (WxUser) items.get(0) : null;
        return user;
    }

    @Override
    public WxUser getUserByIosOpenid(String openId) {
        String hql = "from WxUser where iosOpenid = ?";
        List items = baseDao.queryForList(hql, openId);
        WxUser user = CollectionUtils.isNotEmpty(items) ? (WxUser) items.get(0) : null;
        return user;
    }

    @Override
    public WxUser getUserByAndroidOpenid(String openId) {
        String hql = "from WxUser where androidOpenid = ?";
        List items = baseDao.queryForList(hql, openId);
        WxUser user = CollectionUtils.isNotEmpty(items) ? (WxUser) items.get(0) : null;
        return user;
    }

    @Override
    public WxUser getUserByWechatOpenid(String openId) {
        String hql = "from WxUser where openId = ?";
        List items = baseDao.queryForList(hql, openId);
        WxUser user = CollectionUtils.isNotEmpty(items) ? (WxUser) items.get(0) : null;
        return user;
    }

    public WxUser getByOpenIdAndPublicNumberId(String openId, Long publicNumberId) {
        String hql = "from WxUser user where user.openid=? and user.publicNumberId=? order by user.firstTime asc";
        return (WxUser) baseDao.getUnique(hql, openId, publicNumberId);
    }


    public WxUser getByAuth2(String openId, String appId, String appSecret, HttpServletRequest request) throws Exception {
        String token = WechatUtils.getBaseAccessToken(appId, appSecret, Constant.GRANT_TYPE_CLIENT_CREDENTIAL, request);
        WxUser user = WechatUtils.getUserInformationFromUnion(token, openId, Constant.LANGUAGE_LANG_TYPE_ZH_CN);
        return user;
    }


    @Override
    public WxUser getById(Long userId) {
        if (userId == null) {
            return null;
        }
        if (USER_CACHE.containsKey(userId)) {
            Map cache = USER_CACHE.get(userId);
            long cacheTime = (long) cache.get("time");
            if (System.currentTimeMillis() - cacheTime <= 5 * 1000 * 60) {
                return (WxUser) cache.get("user");
            }
        }
        WxUser user = (WxUser) baseDao.getById(WxUser.class, userId);
        Map userCache = new HashMap();
        userCache.put("time", System.currentTimeMillis());
        userCache.put("user", user);
        return (WxUser) baseDao.getById(WxUser.class, userId);
    }

    @Override
    public List<DBObject>  getByTypeAndUserId(Map params, Integer pageSize, Integer pageNum) throws Exception{

        if (pageNum != null && pageSize != null) {
            return (List<DBObject>) mongoBaseDao.getPageList(params, DBObject.class, pageSize, pageNum, MongoTable.user, new BasicDBObject("regtime", Sort.Direction.DESC.toString()));
        } else {
            return (List<DBObject>) mongoBaseDao.getList(params, DBObject.class, MongoTable.user, new BasicDBObject("regtime", Sort.Direction.DESC.toString()));
        }
//        String hql = "from WxUser u where ";
//        List<Object> condition = new ArrayList<>();
//        condition.add(userType);
//        if (userType == 3) {
//            condition.add(4);
//            hql += "( u.userType=? OR u.userType=? )";
//        } else {
//            hql += " u.userType=? ";
//        }
//        if (StringUtils.isNotEmpty(keyword)) {
//            hql += " AND ( u.nickname like '%" + keyword + "%' OR u.telephone = ? )";
//            condition.add(keyword);
//        }
//
//        if (shopUid != null) {
//            hql += " AND u.firstShopId=?";
//            condition.add(shopUid);
//        }
//        if (StringUtils.isNotEmpty(startTime)) {
//            hql += " AND u.firstTime >= ?";
//            condition.add(DateUtils.StringToDate(startTime));
//        }
//        if (StringUtils.isNotEmpty(endTime)) {
//            hql += " AND u.firstTime <= ?";
//            condition.add(DateUtils.StringToDate(endTime));
//        }
//        if (reviewStatus != null) {
//            hql += " AND u.reviewStatus = ?";
//            condition.add(reviewStatus);
//        }
//        hql += " order by u.firstTime desc";
//        Object[] objects = new Object[condition.size()];
//        condition.toArray(objects);
//        if (pageSize != null && pageNum != null) {
//            return (List<WxUser>) baseDao.pageQuery(hql, pageNum, pageSize, objects);
//        } else {
//            return (List<WxUser>) baseDao.queryForList(hql, objects);
//        }
    }

    @Override
    public long countUser(Map params) {
        return mongoBaseDao.count(params, MongoTable.user);
    }

    @Override
    public DBObject getUserAuthInfo(Long userId) {

        WxUser user = (WxUser) baseDao.getById(WxUser.class, userId);
        if (user == null) {
            throw new BizException("未找到相关用户信息，获取认证资料失败！");
        }
        Integer userType = user.getUserType();
        String table = null;
        switch (userType) {
            case 1:
                throw new BizException("普通用户没有认证资料!");
            case 2:
                if (user.getReviewStatus() == 0) {
                    throw new BizException("用户尚未提交认证资料！");
                }
                table = MongoTable.engineer_auth_info;
                break;
            case 3:
            case 4:
                if (user.getReviewStatus() == 0) {
                    throw new BizException("用户尚未提交认证资料！");
                }
                table = MongoTable.merchant_auth_info;
                break;
            default:
                throw new BizException("未知的用户类型，认证资料获取失败!");
        }
        MCondition mc = MCondition.create(MRel.and);
        mc.append("id", userId);
        DBObject authInfo = (DBObject) mongoBaseDao.findOne(mc.toDBObject().toMap(), DBObject.class, table);
        return authInfo;
    }

    @Override
    public void insertUserAuthInfo(Long userId, DBObject authInfo, Integer targetUserType) throws Exception {
        WxUser user = (WxUser) baseDao.getById(WxUser.class, userId);
        if (user == null) {
            throw new BizException("未找到相关用户信息，添加认证资料失败！");
        }
        String table = null;
        checkUserTypeForAuth(user, targetUserType);
        switch (targetUserType) {
            case 1:
                throw new BizException("普通用户不需要提交认证资料!");
            case 2:
                table = MongoTable.engineer_auth_info;
                break;
            case 3:
            case 4:
                table = MongoTable.merchant_auth_info;
                break;
            default:
                throw new BizException("未知的用户类型，添加认证资料失败!");
        }
        MCondition mc = MCondition.create(MRel.and);
        mc.append("user_id", userId);
        DBObject authInfoExistsObject = (DBObject) mongoBaseDao.findOne(mc.toDBObject().toMap(), DBObject.class, table);
        if (authInfoExistsObject != null) {
            changeUserAuthInfo(userId, authInfo.toMap(), targetUserType);
        } else {
            authInfo.put("user_id", userId);
            mongoBaseDao.insert(authInfo, table);
            user.setReviewStatus(1);
        }
    }

    @Override
    public void changeUserAuthInfo(Long userId, Map params, Integer targetUserType) {
        WxUser user = (WxUser) baseDao.getById(WxUser.class, userId);
        if (user == null) {
            throw new BizException("未找到相关用户信息，更新认证资料失败！");
        }
        checkUserTypeForAuth(user, targetUserType);
        String table = null;
        switch (targetUserType) {
            case 1:
                throw new BizException("普通用户不需要提交认证资料!");
            case 2:
                table = MongoTable.engineer_auth_info;
                break;
            case 3:
            case 4:
                table = MongoTable.merchant_auth_info;
                break;
            default:
                throw new BizException("未知的用户类型，更新认证资料失败!");
        }
        MCondition mc = MCondition.create(MRel.and);
        mc.append("user_id", userId);
        DBObject authInfoExistsObject = (DBObject) mongoBaseDao.findOne(mc.toDBObject().toMap(), DBObject.class, table);
        if (authInfoExistsObject != null) {
            mongoBaseDao.updateOne(mc.toDBObject().toMap(), params, table);
        }

    }

    @Override
    public List getAllMerchantUser() {
        String hql = "from WxUser where userType = 3 or userType = 4";
        List<WxUser> users = (List<WxUser>) baseDao.queryForList(hql);
        List<Map<String, Object>> list = new ArrayList<>();
        for (WxUser user : users) {
            Map<String, Object> map = new HashedMap();
            map.putAll(JSON.parseObject(JSON.toJSONString(user)));
            map.put("id", (Long) user.getId());
            map.put("nickname", user.getNickname());
            list.add(map);
        }
        return list;
    }

    @Override
    public List<WxUser> getAllMerchantUserFull() {
        String hql = "from WxUser where userType = 3 or userType = 4";
        List<WxUser> users = (List<WxUser>) baseDao.queryForList(hql);
        return users;
    }

    @Override
    public WxUser getMerchantByPhone(String phone) {
        if (StringUtils.isEmpty(phone)) {
            throw new BizException("手机号码不能为空!");
        }
        String hql = "from WxUser u where u.telephone=? AND (u.userType=3 OR u.userType=4)";

        return (WxUser) baseDao.getUnique(hql, phone);
    }

    @Override
    public WxUser updateUserInfo(Map userInfo, WxUser user) {

        if (!CommonUtils.isEmpty(userInfo.get("telephone")) && StringUtils.isEmpty(user.getTelephone())) {
            //TODO 如果是更新手机号码 需要核对一下数据库里面是否已经存在了手机号码，如果存在，则合并
            WxUser userInDb = getUserByPhone((String) userInfo.get("telephone"));
            if (userInDb == null) {
                user.setTelephone(String.valueOf(userInfo.get("telephone")));
            } else {
                if (user.getId() != userInDb.getId() && StringUtils.isEmpty(user.getTelephone())) {
                    //根据手机能检索到老用户 并且是初次更新手机号码，那么更新老用户的信息
                    baseDao.delete(user);
                    user = userInDb;
                }
            }

        }
        if (!CommonUtils.isEmpty(userInfo.get("nickname"))) {
            user.setNickname(String.valueOf(userInfo.get("nickname")));
        }
        if (!CommonUtils.isEmpty(userInfo.get("headimgurl"))) {
            user.setHeadimgurl(String.valueOf(userInfo.get("headimgurl")));
        }
        baseDao.update(user);
        return user;
    }

    @Override
    public WxUser getUserByUnionId(String unionId) {
        String hql = "from WxUser where unionId = ?";
        return (WxUser) baseDao.getUnique(hql, unionId);
    }

    @Override
    public WxUser getUserByAppOpenId(String openId) {
        String hql = "from WxUser u where u.iosOpenid = ? or u.androidOpenid = ? order by u.firstTime asc";
        return (WxUser) baseDao.getUnique(hql, openId, openId);
    }

    @Override
    public WxUser updateOldUserInfo(WxUser user, String phone, String state) throws BizException {
        String hql = "from WxUser u where u.telephone = ? order by u.firstTime asc";
        WxUser userInDb = (WxUser) baseDao.getUnique(hql, phone);
        if (CommonUtils.isEmpty(userInDb)) {
            user.setTelephone(phone);
            baseDao.update(user);
        } else {
            //没有找到其他用户，直接返回
            if (new Long(user.getId()).equals(userInDb.getId())) {
                return user;
            }
            LOGGER.info("新用户" + user.getNickname() + "[" + state + "]" + "使用手机：{}合并老平台商户:{}", phone, userInDb.getNickname());

            if (CommonUtils.isEmpty(userInDb.getHeadimgurl())) {
                userInDb.setHeadimgurl(user.getHeadimgurl());
            }

            if (CommonUtils.isEmpty(userInDb.getNickname())) {
                userInDb.setNickname(user.getNickname());
            }

            if (CommonUtils.isEmpty(userInDb.getCountry())) {
                userInDb.setCountry(user.getCountry());
            }

            if (CommonUtils.isEmpty(userInDb.getCity())) {
                userInDb.setCity(user.getCity());
            }

            if (CommonUtils.isEmpty(userInDb.getProvince())) {
                userInDb.setProvince(user.getProvince());
            }

            if (CommonUtils.isEmpty(userInDb.getRemark())) {
                userInDb.setRemark(user.getRemark());
            }

            if (CommonUtils.isEmpty(userInDb.getSubscribed())) {
                userInDb.setSubscribed(user.getSubscribed());
            }

            if (CommonUtils.isEmpty(userInDb.getPrivilege())) {
                userInDb.setPrivilege(user.getPrivilege());
            }
            if (MainConfig.weChatApp.equals(state)) {
                userInDb.setOpenid(user.getOpenid());
            } else if (MainConfig.iosApp.equals(state)) {
                userInDb.setIosOpenid(user.getIosOpenid());
            } else if (MainConfig.androidApp.equals(state)) {
                userInDb.setAndroidOpenid(user.getAndroidOpenid());
            } else {
                throw new BizException("未知的客户端，无法完成身份识别!");
            }
            userInDb.setToken(user.getToken());

            String hxUsername = "wx_" + userInDb.getId();
            String hxPassword = Base64.encodeToString(String.valueOf(userInDb.getId()).getBytes());
            HuanXinSdkUtil.registUser(hxUsername, hxPassword, userInDb.getNickname());
            userInDb.setHxUsername(hxUsername);
            userInDb.setHxPassword(hxPassword);


            if (CommonUtils.isEmpty(userInDb.getLongitude())) {
                userInDb.setLongitude(user.getLongitude());
            }
            if (CommonUtils.isEmpty(userInDb.getLatitude())) {
                userInDb.setLatitude(user.getLatitude());
            }

            baseDao.delete(user);
            baseDao.update(userInDb);

        }
        return user;
    }

    @Override
    public WxUser getUserByPhone(String phone) {
        String hql = "from WxUser where telephone = ?";
        return (WxUser) baseDao.getUnique(hql, phone);
    }

    @Override
    public WxUser getByHxUserName(String userName) {
        String hql = "from WxUser where hxUsername=?";
        return (WxUser) baseDao.getUnique(hql, userName);
    }

    @Override
    public List<WxUser> getAllUser() {
        return (List<WxUser>) baseDao.getAll(WxUser.class);
    }


    private void checkUserTypeForAuth(WxUser user, Integer targetUserType) {
        if (user.getUserType() != 1) {
            if (user.getUserType() == 2) {
                if (targetUserType == 3 || targetUserType == 4) {
                    throw new BizException("您的身份是维修技师，无法再提交商户的认证！");
                }
            } else if (user.getUserType() == 3 || user.getUserType() == 4) {
                if (targetUserType == 2) {
                    throw new BizException("您的身份是商户，无法再提交维修技师的认证！");

                }
            }
        }
    }


}
